package com.xtjc.recsyslog.netty;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.http.HttpUtil;
import com.xtjc.recsyslog.netty.analyze.AnalyzeYunCharge;
import com.xtjc.recsyslog.netty.entity.ChargeEntity;
import com.xtjc.recsyslog.netty.util.NettyUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.Attribute;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;
import java.util.HashMap;


@Component
@ChannelHandler.Sharable
@Slf4j
public class NettyHandler extends SimpleChannelInboundHandler<String[]> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String[] sourceMsg) throws Exception {
        try {
//            log.info("接收16进制原数据：{}", (Object) sourceMsg);
            AnalyzeYunCharge analyzeYunCharge = AnalyzeYunCharge.analyzeYunCharge(sourceMsg);
            String msgType = analyzeYunCharge.getMsgType();
            switch (msgType) {
                case "01":
                    R01(analyzeYunCharge, ctx);//充电桩登录认证
                    break;
                case "03":
                    R03(analyzeYunCharge, ctx);//充电桩心跳包
                    break;
                case "05":
                    R05(analyzeYunCharge, ctx);//计费模型验证请求
                    break;
                case "09":
                    R09(analyzeYunCharge, ctx);//充电桩计费模型请求
                    break;
                case "13":
                    R13(analyzeYunCharge);//上传实时监测数据
                    break;
                case "33":
                    R33(analyzeYunCharge);//远程启动充电命令回复
                    break;
                case "35":
                    R35(analyzeYunCharge);//远程停机命令回复
                    break;
                case "51":
                    R51(analyzeYunCharge);//充电桩工作参数设置应答
                    break;
                case "55":
                    R55(analyzeYunCharge);//对时设置应答
                    break;
                case "3B"://交易记录
                    R59(analyzeYunCharge, ctx);
                    break;
                case "91":
                    R91(analyzeYunCharge);//远程重启应答
                    break;
                case "F1":
                    RF1(analyzeYunCharge);//桩应答远程下发二维码前缀指令
                    break;
                default:
                    Rdefault(analyzeYunCharge, ctx);//打印默认值
                    break;
            }
//            log.info("receive end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
        String ip = address.toString();
        log.info("新客户端接入channelActive -->  RamoteAddress : {} connected ", ip);
    }

    // 客户端断开
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        Attribute<ChargeEntity> chargeInfo = ctx.attr(NettyUtil.NETTY_CHANNEL_KEY);
        ChargeEntity data = chargeInfo.get();
        NettyUtil.removeChannel(data.getSnBCD());
        log.debug("设备断开连接");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("异常关闭 exceptionCaught : {} ctx = {}", cause.toString(), ctx.channel().toString());
        cause.printStackTrace();
        Attribute<ChargeEntity> chargeInfo = ctx.attr(NettyUtil.NETTY_CHANNEL_KEY);
        ChargeEntity data = chargeInfo.get();
        NettyUtil.removeChannel(data.getSnBCD());
        ctx.close();
    }

    private void RF1(AnalyzeYunCharge analyzeYunCharge) {
//        log.info("桩应答远程下发二维码前缀指令");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
        String status = data[7];
//        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("下发结果：{}", status);
    }



    private void R59(AnalyzeYunCharge analyzeYunCharge, ChannelHandlerContext ctx) {
        log.info("==========================交易记录==========================");
        String[] data = analyzeYunCharge.getData();
        //交易流水号 16
        String[] txnSeqNum = ArrayUtil.sub(data, 0, 16);
        //桩编号 7
        String[] sn = ArrayUtil.sub(data, 16, 23);
        //枪号 1
        String gNum = data[23];
        //开始时间 7
        String[] startTime = ArrayUtil.sub(data, 24, 31);
        //结束时间 7
        String[] endTime = ArrayUtil.sub(data, 31, 38);
        //尖单价 4
        String[] A = ArrayUtil.sub(data, 38, 42);
        //尖电量 4
        String[] A_1 = ArrayUtil.sub(data, 42, 46);
        //计损尖电量 4
        String[] A_2 = ArrayUtil.sub(data, 46, 50);
        //尖金额 4
        String[] A_M = ArrayUtil.sub(data, 50, 54);
        //峰单价 4
        String[] B = ArrayUtil.sub(data, 54, 58);
        //峰电量 4
        String[] B_1 = ArrayUtil.sub(data, 58, 62);
        //计损峰电量 4
        String[] B_2 = ArrayUtil.sub(data, 62, 66);
        //峰金额 4
        String[] B_M = ArrayUtil.sub(data, 66, 70);
        //平单价 4
        String[] C = ArrayUtil.sub(data, 70, 74);
        //平电量 4
        String[] C_1 = ArrayUtil.sub(data, 74, 78);
        //计损平电量 4
        String[] C_2 = ArrayUtil.sub(data, 78, 82);
        //平金额 4
        String[] C_M = ArrayUtil.sub(data, 82, 86);
        //谷单价 4
        String[] D = ArrayUtil.sub(data, 86, 90);
        //谷电量 4
        String[] D_1 = ArrayUtil.sub(data, 90, 94);
        //计损谷电量 4
        String[] D_2 = ArrayUtil.sub(data, 94, 98);
        //谷金额 4
        String[] D_M = ArrayUtil.sub(data, 98, 102);
        //电表总起值 5
        String[] start_total = ArrayUtil.sub(data, 102, 107);
        //电表总止值 5
        String[] end_total = ArrayUtil.sub(data, 107, 112);
        //总电量 4
        String[] total = ArrayUtil.sub(data, 112, 116);
        total = this.changeDesc(total);
        //计损总电量 4
        String[] total_1 = ArrayUtil.sub(data, 116, 120);
        //消费金额 4
        String[] money = ArrayUtil.sub(data, 120, 124);
        //电动汽车唯一标识 17
        String[] vin = ArrayUtil.sub(data, 124, 141);
        //交易标识 1
        String code = data[141];
        //交易日期、时间  7
        String[] tradingTime = ArrayUtil.sub(data, 142, 149);
        //停止原因 30
        String stopReason = data[149];
        //物理卡号  8
        String[] wl_card_no = ArrayUtil.sub(data, 150, 158);


//        log.info("data:{}", ArrayUtil.toString(data));
        log.info("交易流水号：{}", ArrayUtil.toString(txnSeqNum));
        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("枪号：{}", gNum);
//        log.info("开始时间：{}", ArrayUtil.toString(startTime));
//        log.info("结束时间:{}", ArrayUtil.toString(endTime));

//        log.info("尖单价:{}", ArrayUtil.toString(A));
//        log.info("尖电量:{}", ArrayUtil.toString(A_1));
//        log.info("计损尖电量:{}", ArrayUtil.toString(A_2));
//        log.info("尖金额:{}", ArrayUtil.toString(A_M));
//
//        log.info("峰单价:{}", ArrayUtil.toString(B));
//        log.info("峰电量:{}", ArrayUtil.toString(B_1));
//        log.info("计损峰电量:{}", ArrayUtil.toString(B_2));
//        log.info("峰金额:{}", ArrayUtil.toString(B_M));

//        log.info("平单价:{}", ArrayUtil.toString(C));
//        log.info("平电量:{}", ArrayUtil.toString(C_1));
//        log.info("计损平电量:{}", ArrayUtil.toString(C_2));
//        log.info("平金额:{}", ArrayUtil.toString(C_M));

//        log.info("谷单价:{}", ArrayUtil.toString(D));
//        log.info("谷电量:{}", ArrayUtil.toString(D_1));
//        log.info("计损谷电量:{}", ArrayUtil.toString(D_2));
//        log.info("谷金额:{}", ArrayUtil.toString(D_M));

//        log.info("电表总起值:{}", ArrayUtil.toString(start_total));
//        log.info("电表总止值:{}", ArrayUtil.toString(end_total));
        log.info("总电量:{}", this.changeH2D(total,10000));
//        log.info("计损总电量:{}", ArrayUtil.toString(total_1));
//        log.info("消费金额:{}", ArrayUtil.toString(money));
//        log.info("电动汽车唯一标识:{}", ArrayUtil.toString(vin));
//        log.info("交易标识:{}", code);
//        log.info("交易日期、时间:{}", ArrayUtil.toString(tradingTime));
        log.info("停止原因:{}", stopReason);
//        log.info("物理卡号:{}", ArrayUtil.toString(wl_card_no));
        String[] resData = AnalyzeYunCharge.tradingAck(analyzeYunCharge);
        ctx.writeAndFlush(resData);
        log.info("==========================================================");
    }

    private void R35(AnalyzeYunCharge analyzeYunCharge) {
        log.info("==========================远程停机命令回复==========================");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
        String gNum = data[7];
        String res = data[8];
        String reason = data[9];
//        log.info("data:{}", ArrayUtil.toString(data));
        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("枪号：{}", gNum);
//        log.info("停止结果：{}", res);
        log.info("失败原因:{}", reason);
        //0x00 无;  0x01 设备编号不匹配;  0x02 枪未处于充电状态;  0x03 其他
        log.info("=================================================================");
    }

    private void R33(AnalyzeYunCharge analyzeYunCharge) {
        log.info("==========================远程启动充电命令回复==========================");
        String[] data = analyzeYunCharge.getData();
        String[] txnSeqNum = ArrayUtil.sub(data, 0, 16);
        String[] sn = ArrayUtil.sub(data, 16, 23);
        String gNum = data[23];
        String status = data[24];
        String re = data[25];
//        log.info("data:{}", ArrayUtil.toString(data));
        log.info("交易流水号：{}", ArrayUtil.toString(txnSeqNum));
        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("启动结果：{}", status);
//        log.info("枪号：{}", gNum);
        log.info("失败原因:{}", re);
        //0x00无;   0x01设备编号不匹配;   0x02枪已在充电;   0x03设备故障;   0x04设备离线;  0x05未插枪
        log.info("====================================================================");
    }

    private void R91(AnalyzeYunCharge analyzeYunCharge) {
        log.info("远程重启应答");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
        String res = data[7];
        log.info("桩编码：{}", ArrayUtil.toString(sn));
        log.info("设置结果：{}", res);
    }

    private void R55(AnalyzeYunCharge analyzeYunCharge) {
//        log.info("---------------------对时设置应答---------------------");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
        String[] time = ArrayUtil.sub(data, 7, 14);
//        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("当前时间：{}", Cp56Time2aUtil.toDate(ConverUtil.hexArrayToByteArray(time)));
    }

    private void R51(AnalyzeYunCharge analyzeYunCharge) {
//        log.info("---------------------充电桩工作参数设置应答---------------------");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
        String res = data[7];
//        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("设置结果：{}", res);
    }

    private String[] changeDesc(String[] values){
        String[] returnValues = new String[values.length];
        for (int i = 0; i < values.length; i++){
            returnValues[values.length-1-i] = values[i];
        }
        return returnValues;
    }

    private void R13(AnalyzeYunCharge analyzeYunCharge) {
        log.info("====================上传实时监测数据====================");
        String[] data = analyzeYunCharge.getData();
        //交易流水号 16
        String[] serialNumber = ArrayUtil.sub(data, 0, 16);
        //桩编号 7
        String[] sn = ArrayUtil.sub(data, 16, 23);
        //枪号 1
//        String gunNumber = data[23];
        //状态 1
        String chargeStatus = data[24]; //1   //0x00：离线 ,0x01：故障 ,0x02：空闲 ,0x03：充电  --- 需做到变位上送
        //枪是否归位 1
//        String gunStatus = data[25]; //1  //0x00 否 0x01 是 0x02 未知  （无法检测到枪是否插回枪座即未知）
        //是否插枪 1
//        String sfcq = data[26];//0x00 否 0x01 是
        //输出电压 2
        String[] outVoltage = ArrayUtil.sub(data, 27, 29);
        outVoltage = this.changeDesc(outVoltage);
        //输出电流 2
        String[] outElectricity = ArrayUtil.sub(data, 29, 31);
        outElectricity = this.changeDesc(outElectricity);
        //枪线温度 1
//        String gunT = data[31];//整形，偏移量-50；待机置零
        //枪线编码 8
//        String[] gunCode = ArrayUtil.sub(data, 32, 40);//没有置零
        //SOC 1
//        String SOC = data[40];//待机置零；交流桩置零
        //电池组最高温度 1
//        String higT = data[41];
        //累计充电时间 2
        String[] chargingTime = ArrayUtil.sub(data, 42, 44);//单位：min；待机置零
        chargingTime = this.changeDesc(chargingTime);
//        //剩余时间  2
//        String[] remaining = ArrayUtil.sub(data, 44, 46);//单位：min；待机置零、交流桩置零
        //充电度数  4
        String[] chargingDegree = ArrayUtil.sub(data, 46, 50);//精确到小数点后四位；待机置零
        chargingDegree = this.changeDesc(chargingDegree);
//        //计损充电度数 4
//        String[] jscdds = ArrayUtil.sub(data, 50, 54);
//        //已充金额  4
//        String[] ycje = ArrayUtil.sub(data, 54, 58);
        //硬件故障 2
        String[] yjgz = ArrayUtil.sub(data, 58, 60);

//        log.info("data:{}",ArrayUtil.toString(data));
        log.info("交易流水号：{}", ArrayUtil.toString(serialNumber));
        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("枪号：{}", gunNumber);
        log.info("状态：{}", chargeStatus);
        //0x00：离线  ,0x01：故障,  0x02：空闲,  0x03：充电
//        log.info("枪是否归位：{}", gunStatus);
//        log.info("是否插枪：{}", sfcq);
//        log.info("输出电压：{}", this.changeH2D(outVoltage,10));
//        log.info("输出电流：{}", this.changeH2D(outElectricity,10));
//        log.info("枪线温度：{}", gunT);
//        log.info("枪线编码：{}", ArrayUtil.toString(gunCode));
//        log.info("SOC：{}", SOC);
//        log.info("电池组最高温度：{}", higT);
        log.info("累计充电时间：{}", this.changeH2D(chargingTime,1));
//        log.info("剩余充电时间：{}", ArrayUtil.toString(remaining));
//        log.info("充电度数：{}", ArrayUtil.toString(chargingDegree));
        log.info("充电度数：{}", this.changeH2D(chargingDegree,10000));
//        log.info("计损充电度数：{}", ArrayUtil.toString(jscdds));
//        log.info("已充金额：{}", ArrayUtil.toString(ycje));
        log.info("硬件故障：{}", ArrayUtil.toString(yjgz));
        log.info("=====================================================");
    }

    private double changeH2D(String[] arr,int d){
        String value = String.join("", arr);
        int sum = 0;
        for(int i = 0; i < value.length(); i++){
            String sub = value.substring(i,i+1);
            if(sub.equals("A")) {
                sub = "10";
            }
            else if(sub.equals("B")) {
                sub = "11";
            }
            else if(sub.equals("C")) {
                sub = "12";
            }
            else if(sub.equals("D")) {
                sub = "13";
            }
            else if(sub.equals("E")) {
                sub = "14";
            }
            else if(sub.equals("F")) {
                sub = "15";
            }
            int v = Integer.parseInt(sub);
            sum += v * Math.pow(16,value.length()-i-1);
        }
        return Double.parseDouble(sum+"")/d;
    }

    private void R09(AnalyzeYunCharge analyzeYunCharge, ChannelHandlerContext ctx) {
//        log.info("充电桩计费模型请求");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
//        log.info("桩编码：{}", ArrayUtil.toString(sn));
        String[] resData = AnalyzeYunCharge.chargeModelAck(analyzeYunCharge);
        ctx.writeAndFlush(resData);
    }

    private void R05(AnalyzeYunCharge analyzeYunCharge, ChannelHandlerContext ctx) {
//        log.info("---------------------计费模型验证请求---------------------");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
        String chargeModel1 = data[7];
        String chargeModel2 = data[8];
//        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("计费模型编码：{},{}", chargeModel1, chargeModel2);
        String[] resData = AnalyzeYunCharge.chargeModelCheckAck(analyzeYunCharge, chargeModel2.equals("07"));
        ctx.writeAndFlush(resData);
    }

    private void R03(AnalyzeYunCharge analyzeYunCharge, ChannelHandlerContext ctx) {
        log.info("====================充电桩心跳包====================");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
        NettyUtil.putChannel(String.join("", sn), new ChargeEntity(sn, ctx.channel()));//测试用
//        String gunNumber = data[7];
        String gunStatus = data[8];
        log.info("桩编码：{}",ArrayUtil.toString(sn));
//        log.info("枪号：{}",gunNumber);
        log.info("枪状态：{}",gunStatus);
        String[] resData = AnalyzeYunCharge.heartBeatAck(analyzeYunCharge);
        ctx.writeAndFlush(resData);
        log.info("==================================================");

    }

    private void R01(AnalyzeYunCharge analyzeYunCharge, ChannelHandlerContext ctx) {
//        log.info("---------------------充电桩登录认证---------------------");
        String[] data = analyzeYunCharge.getData();
        String[] sn = ArrayUtil.sub(data, 0, 7);
        //缓存充电桩channel
        NettyUtil.putChannel(String.join("", sn), new ChargeEntity(sn, ctx.channel()));
        String type = data[7];
//        String num = data[8];
//        String protocol = data[9];
//        String[] version = ArrayUtil.sub(data, 10, 18);
//        String networkType = data[18];
//        String[] sim = ArrayUtil.sub(data, 19, 29);
//        String operator = data[29];
//        log.info("桩编码：{}", ArrayUtil.toString(sn));
//        log.info("桩类型：{}", type);
//        log.info("充电枪数量：{}", num);
//        log.info("通信协议版本：{}", protocol);
//        log.info("程序版本：{}", ArrayUtil.toString(version));
//        log.info("网络链接类型：{}", networkType);
//        log.info("Sim 卡：{}", ArrayUtil.toString(sim));
//        log.info("运营商：{}", operator);
        String[] resData = AnalyzeYunCharge.loginAck(analyzeYunCharge, true);
        ctx.writeAndFlush(resData);

    }


    private void Rdefault(AnalyzeYunCharge analyzeYunCharge, ChannelHandlerContext ctx) {
        log.debug("未知消息类型{}", analyzeYunCharge.getMsgType());
        String[] data = analyzeYunCharge.getData();
        log.debug("接收其他协议值：{}", (Object) data);
    }

}
